package com.blm.service.impl;

import com.blm.common.constants.CacheConstant;
import com.blm.common.constants.ExceptionConstant;
import com.blm.entity.Food;
import com.blm.entity.History;
import com.blm.entity.Store;
import com.blm.exception.CommonException;
import com.blm.repository.FoodRepository;
import com.blm.repository.HistoryRepository;
import com.blm.service.HistoryService;
import com.blm.service.StoreService;
import com.blm.util.RedisUtil;
import com.blm.vo.FoodVO;
import com.blm.vo.PageVO;
import com.blm.vo.StoreVO;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class HistoryServiceImpl implements HistoryService {

    @Autowired
    private HistoryRepository historyRepository;

    @Autowired
    private FoodRepository foodRepository;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private StoreService storeService;

    @Override
    @Cacheable(value = CacheConstant.HISTORY_STORE, key = "#userId + '_' + #page + '_' + #size")
    public PageVO<StoreVO> listStoresHistory(Long userId, int page, int size) {
        PageHelper.startPage(page, size);
        List<History> historyList = historyRepository.findByUserIdOrderedDesc(userId, History.TYPE_STORE);
        
        PageInfo<History> pageInfo = new PageInfo<>(historyList);
        
        List<StoreVO> storeVOList = historyList.stream().map(history -> {
            Store store = storeService.getStoreById(history.getTargetId());
            StoreVO vo = new StoreVO();
            BeanUtils.copyProperties(store, vo);
            vo.setHistoryId(history.getId());
            vo.setVisitedAt(history.getCreatedAt());
            return vo;
        }).collect(Collectors.toList());
        
        return new PageVO<>(page, size, pageInfo.getTotal(), pageInfo.getPages(), storeVOList);
    }

    @Override
    @Cacheable(value = CacheConstant.HISTORY_FOOD, key = "#userId + '_' + #page + '_' + #size")
    public PageVO<FoodVO> listFoodsHistory(Long userId, int page, int size) {
        PageHelper.startPage(page, size);
        List<History> historyList = historyRepository.findByUserIdOrderedDesc(userId, History.TYPE_FOOD);
        
        PageInfo<History> pageInfo = new PageInfo<>(historyList);
        
        List<FoodVO> foodVOList = historyList.stream().map(history -> {
            Food food = foodRepository.findById(history.getTargetId())
                    .orElseThrow(() -> new CommonException(ExceptionConstant.FOOD_NOT_FOUND));
            FoodVO vo = new FoodVO();
            BeanUtils.copyProperties(food, vo);
            // 替换id为历史记录id
            vo.setId(history.getId());
            vo.setVisitedAt(history.getCreatedAt());
            return vo;
        }).collect(Collectors.toList());
        
        return new PageVO<>(page, size, pageInfo.getTotal(), pageInfo.getPages(), foodVOList);
    }

    @Override
    @Transactional
    public void removeHistory(Long userId, Long historyId) {
        // Check if history exists
        History history = historyRepository.findByIdAndUserId(historyId, userId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.HISTORY_NOT_FOUND));

        historyRepository.deleteById(historyId);
        if(Objects.equals(history.getType(), History.TYPE_FOOD)) {
            redisUtil.clearCache(CacheConstant.HISTORY_FOOD, userId);
        } else {
            redisUtil.clearCache(CacheConstant.HISTORY_STORE, userId);
        }
    }

    @Override
    @Transactional
    public void addStoreHistory(Long userId, Long storeId) {
        // Check if store exists
        storeService.getStoreById(storeId);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime earliest = now.toLocalDate().atStartOfDay();
        // Check if there is already a record for today
        Optional<History> recordInTheDay = historyRepository.findRecordInTheDay(userId, History.TYPE_STORE, earliest);
        if (recordInTheDay.isPresent()) {
            // If exists, update the existing record
            historyRepository.updateVisitedTime(recordInTheDay.get().getId(), now);
        }else{
            historyRepository.insert(userId, storeId, History.TYPE_STORE);
        }

        redisUtil.clearCache(CacheConstant.HISTORY_STORE, userId);
    }

    @Override
    @Transactional
    public void addFoodHistory(Long userId, Long foodId) {
        // Check if food exists
        foodRepository.findById(foodId)
                .orElseThrow(() -> new CommonException(ExceptionConstant.FOOD_NOT_FOUND));
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime earliest = now.toLocalDate().atStartOfDay();
        // Check if there is already a record for today
        Optional<History> recordInTheDay = historyRepository.findRecordInTheDay(userId, History.TYPE_FOOD, earliest);
        if (recordInTheDay.isPresent()) {
            // If exists, update the existing record
            historyRepository.updateVisitedTime(recordInTheDay.get().getId(), now);
        } else {
            historyRepository.insert(userId, foodId, History.TYPE_FOOD);
        }

        redisUtil.clearCache(CacheConstant.HISTORY_FOOD, userId);
    }
}
